/* Includes ------------------------------------------------------------------*/
#include "trigger.h"
#include <stddef.h>
/* typedef -------------------------------------------------------------------*/
/* define --------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
/* function prototypes -------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/
/*********************************************************************************************

  * @brief  Trigger_Init
  * @param  *trigger:   trigger object pointer
  *         *obj:       the driver obj pointer of trigger hardware, use this param to get status of trigger in hardware layer
  *         type:       the type of driver obj, distinguish between different kind of trigger, like io key, i2c touch key
  *         id:         the integer tag of this trigger, 
  *         read:       the function of read if trigger is triggered
  *         timer:      get system timer to calculate the duration of trig and hold
  * @return 
  * @remark 

  ********************************************************************************************/
void Trigger_Init(struct Trigger *triggerObj, 
                  void      *obj,
                  uint32_t  type, 
                  int       id,
                  int      (*read)(void *io, uint32_t pin),
                  uint32_t  (*timer)(void))
{
    triggerObj->driver.obj = obj;
    triggerObj->driver.type = type;
    triggerObj->id = id;
    triggerObj->ops.read = read;
    triggerObj->ops.timer = timer;
}
/*********************************************************************************************

  * @brief  Trigger_RegisterEvent
  * @param  *trigger:       trigger object pointer
  *         *eventHander:   when condition is confirmed, run the function which register for corresponding event
  * @remark the register of trigger event, like trig, release, hold

  ********************************************************************************************/
void Trigger_RegisterEventTrig(struct Trigger *triggerObj, void (*trigger)(int id))
{   triggerObj->event.trigger = trigger;   }
void Trigger_RegisterEventRelease(struct Trigger *triggerObj, void (*release)(int id, int duration))
{   triggerObj->event.release = release;   }
void Trigger_RegisterEventHold(struct Trigger *triggerObj, void (*hold)(int id), int timeout)
{   
    triggerObj->event.hold = hold;   
    triggerObj->_timeoutHold = timeout;
}


/*********************************************************************************************

  * @brief  Trigger_Check
  * @param  *trigger:   trigger object pointer
  * @return NULL
  * @remark check if the trigger is triged, then tag the status of trigger to trigged and save current time

  ********************************************************************************************/
 void Trigger_Check(struct Trigger *triggerObj)
{
    if(triggerObj->ops.read(triggerObj->driver.obj, triggerObj->driver.type) == 1 
       && triggerObj->_isTriggered == false)
    {	
        triggerObj->_isTriggered = true;	
        triggerObj->_timeTriggerStart = triggerObj->ops.timer();
        if(triggerObj->event.trigger != NULL)
        {   triggerObj->event.trigger(triggerObj->id);   }
    }
}
/*********************************************************************************************

  * @brief  Trigger_Process
  * @param  *trigger:   trigger object pointer
  * @return NULL
  * @remark check if the trigger is triged, released and hold timeout, then run corresponding event

  ********************************************************************************************/
void Trigger_Process(struct Trigger *triggerObj)
{
    int status = 0;

    if(triggerObj->_isTriggered == false)
    {
        /* trigger event check */
        if(triggerObj->ops.read(triggerObj->driver.obj, triggerObj->driver.type) == 1)
        {	
            triggerObj->_isTriggered = true;	
            triggerObj->_timeTriggerStart = triggerObj->ops.timer();
            if(triggerObj->event.trigger != NULL)
            {   triggerObj->event.trigger(triggerObj->id);   }
        }
    }
    else
    {
        /* status is -1 means the result of trigger check is invalid, throw it */
        status = triggerObj->ops.read(triggerObj->driver.obj, triggerObj->driver.type);

        /* release event check */
        if(status == 0)
        {
            triggerObj->_isTriggered = false;
            triggerObj->_hasHoldTimeout = false;
            if(triggerObj->event.release != NULL)
            {   triggerObj->event.release(triggerObj->id, triggerObj->ops.timer() - triggerObj->_timeTriggerStart); }
        }
        /* hold timeout event check, when the hold duration is over specified timeout, then run hold event */
        else if(status == 1 
                && triggerObj->event.hold != 0 
                && triggerObj->_timeoutHold != 0
                && triggerObj->_hasHoldTimeout == false
                && (triggerObj->ops.timer() - triggerObj->_timeTriggerStart) > triggerObj->_timeoutHold)
        {
            triggerObj->event.hold(triggerObj->id);
            triggerObj->_hasHoldTimeout = true;
        }
    }
}
/*********************************************************************************************

  * @brief  Trigger_IsTriggering
  * @param  *trigger:   trigger object pointer
  * @return true: is triggering
  *         false: is released
  * @remark check if the trigger is triggering

  ********************************************************************************************/
bool Trigger_IsTriggering(struct Trigger *triggerObj)
{   return triggerObj->_isTriggered;    }
